#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>

#define MAXSIZE     256

int main(void)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); //创建socket ipv4协议 流式套接字 具体的协议类型
    if (-1 == sockfd) { perror("socket"); exit(1); }

    int opt = 1;
    setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));   //地址可以被重复绑定

    struct sockaddr_in server_addr;    //保存服务器的信息
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8000);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //127.0.0.1回环ip 表示本机 测试时候可以用

    int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //绑定信息
    if (-1 == ret) { perror("bind"); exit(1); }

    ret = listen(sockfd, 10); //设置监听队列
    if (-1 == ret) { perror("listen"); exit(1); }

    int epfd = epoll_create(MAXSIZE);   //创建epoll对象
    if (-1 == epfd) { perror("epoll_create"); exit(1); }

    struct epoll_event ev, events[MAXSIZE] = {0};
    ev.events = EPOLLIN;       //监听sockfd可读事件
    ev.data.fd = sockfd;
    if(0 > epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev)){ perror("epoll_ctl"); exit(1); }

    struct sockaddr_in client_addr;   //用于保存客户端的信息
    socklen_t length = sizeof(client_addr);
    char buf[32] = {0};

    while (1) {
        int num = epoll_wait(epfd, events, MAXSIZE, -1);   //-1表示阻塞
        if (-1 == num) { perror("epoll_wait"); exit(1); } //出错就退出
        //没有出错表示有文件描述符可读
        for (int i = 0; i < num; i++) {
            if (events[i].data.fd == sockfd) { //有客户端发起连接
                int fd = accept(sockfd, (struct sockaddr *)&client_addr, &length);
                if (-1 == fd) { perror("accept"); exit(1); }

                printf("接受来自%s的连接fd=%d\n", inet_ntoa(client_addr.sin_addr), fd);

                //为新的文件描述符注册事件
                ev.data.fd = fd;
                ev.events = EPOLLIN;
                if(0 > epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)){ perror("epoll_ctl"); }
            }
            else {
                if (events[i].events & EPOLLIN){ //如果事件是可读的
                    ret = recv(events[i].data.fd, buf, sizeof(buf), 0);
                    if (-1 == ret) { perror("recv"); }
                    else if (0 == ret) { //客户端退出，注销事件
                        ev.data.fd = events[i].data.fd;
                        ev.events = EPOLLIN;
                        epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
                        close(events[i].data.fd);
                        printf("客户端%d下线!\n", events[i].data.fd);
                    }
                    else { printf("收到%d客户端的消息%s\n", events[i].data.fd, buf); }
                    memset(buf, 0, sizeof(buf));
                }
            }
        }
    }
    return 0;
}
